package com.zone.editor.pixel;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.zone.editor.inter.DrawingView;
import com.zone.editor.pixel.image.ColorItem;
import com.zone.editor.pixel.image.ColorItemPool;
import com.zone.editor.pixel.image.Template;
import com.zone.editor.shape.AbstractShape;
import com.zone.editor.shape.LineShape;
import com.zone.editor.shape.OvalShape;
import com.zone.editor.shape.PixelBrushShape;
import com.zone.editor.shape.RectangleShape;
import com.zone.editor.shape.ShapeAndPaint;
import com.zone.editor.shape.ShapeBuilder;
import com.zone.editor.shape.ShapeType;
import com.zone.editor.utils.NumberUtil;
import com.zone.editor.utils.RomUtil;
import com.zone.sdk.isd.util.ISDColorUtil;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class PixelDrawingView extends DrawingView {

    private static final String TAG = PixelDrawingView.class.getSimpleName();

    protected static final Stack<ShapeAndPaint> drawShapes = new Stack<>();
    protected static final Stack<ShapeAndPaint> redoShapes = new Stack<>();

    private int radius = 1;
    private int widthNum, heightNum;

    private RectF staticRect;
    private int staticColor;

    private Bitmap templateBitmap;
    private int[] templateColor;
    private List<Integer> blankColor;
    private Map<Integer, Point> cacheColorIndex = new HashMap<>();
    private Map<Integer, Integer> hitCache = new HashMap<>();
    private float density = 1;
    private float times = 1;
    private boolean isFront = true;

    // region constructors
    public PixelDrawingView(Context context) {
        this(context, null);
    }

    public PixelDrawingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PixelDrawingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        widthNum = w / radius;
        heightNum = h / radius;

        float length = widthNum * heightNum;
        int templateWith = templateBitmap.getWidth();
        int templateHeight = templateBitmap.getHeight();
        int templateLength = templateWith * templateHeight;
        if (widthNum >= templateWith) {
            times = widthNum / templateWith;
        } else {
            times = templateWith / widthNum;
        }
        Log.e(TAG, "times=" + times + " widthNum=" + widthNum + " templateWith=" + templateWith);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure " + widthMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (ShapeAndPaint shape : drawShapes) {
            shape.getShape().draw(canvas, shape.getPaint());
        }
    }

    public void setStaticRect(RectF rect, int color) {
        staticRect = rect;
        staticColor = color;
    }

    @Override
    public void onTouchEventDown(float touchX, float touchY) {
        Log.e(TAG, "touchX="+touchX+" touchY="+touchY);
        createShape();
        if (currentShape != null && currentShape.getShape() != null) {
            currentShape.getShape().startShape(touchX, touchY);
        }
    }

    @Override
    public void onTouchEventMove(float xf, float yf) {
        Log.e(TAG, "xf="+xf+" yf="+yf);
        if (xf < 0 || yf < 0){
            return;
        }
        // 模板匹配画笔能力
        if (templateBitmap != null) {
            int searchCount = 1;
            int indexW = 0;
            int indexH = 0;

            // 1、计算图片与模板比例
            int length = widthNum * heightNum;
            int pixelIndex = 0;
            int h;
            int w;
            if (length >= templateColor.length) {
                //放大：布局与模板比
                h = (int) (yf / times);
                w = (int) (xf / times);
            } else {
                //缩小：布局与模板比
                h = (int) (yf * times);
                w = (int) (xf * times);
            }
            h = h + indexH;
            w = w + indexW;
            while (searchCount < 2) {
                pixelIndex = (h - 1) * templateBitmap.getWidth() + w;
                if (pixelIndex < templateColor.length && pixelIndex > 0) {
                    try {
                        int currentTemplateColor = templateColor[pixelIndex];
                        // 排除空白区域
                        if (blankColor.contains(currentTemplateColor)) {
                            searchCount++;
                            if (hitCache.containsKey(currentTemplateColor)){
                                Log.d(TAG, "fit blank hit cache ="+currentTemplateColor);
                                w = w+1;
                                Integer count = hitCache.get(currentTemplateColor);
                                if (count == null){
                                    return;
                                }
                                if (count > 1) {
                                    hitCache.remove(currentTemplateColor);
                                }else {
                                    count++;
                                    hitCache.put(currentTemplateColor, count);
                                }
                                continue;
                            } else {
                                hitCache.put(currentTemplateColor, 1);
                            }
//                            Log.d(TAG, "fit blank pixel searchCount="+searchCount+" pixelIndex="+pixelIndex);
                            Point point = cacheColorIndex.get(pixelIndex);
                            if (point == null){
                                return;
                            }
                            Log.d(TAG, "fit blank pixel cacheColorIndex before w="+w+" h"+h+" after w="+point.x+" h="+point.y);
                            h = point.y;
                            w = point.x;
                            if (length >= templateColor.length) {
                                //放大：布局与模板比
//                                h = (int) (yf / times) + indexH;
//                                w = (int) (xf / times) + indexW;
                                yf = (h - indexH)*times;
                                xf = (w - indexW)*times;
                            } else {
                                //缩小：布局与模板比
//                                h = (int) (yf * times);
//                                w = (int) (xf * times);
                                yf = (h - indexH) / times;
                                xf = (w - indexW) / times;
                            }
                            currentShape.getShape().moveShape(xf, yf);
                            return;
//                            if (searchCount == 1) {
//                                indexH = 1;
//                                indexW = 0;
//                            } else if (searchCount == 2) {
//                                indexH = 0;
//                                indexW = 1;
//                            } else if (searchCount == 3) {
//                                indexH = 1;
//                                indexW = 1;
//                            } else if (searchCount == 4) {
//                                indexH = -1;
//                                indexW = 0;
//                            } else if (searchCount == 5) {
//                                indexH = 0;
//                                indexW = -1;
//                            } else if (searchCount == 6) {
//                                indexH = -1;
//                                indexW = -1;
//                            }
//                            searchCount++;
//                            continue;
                        } else {
                            if (currentShape != null && currentShape.getShape() != null) {
                                int uiColor = ISDColorUtil.getIsdUIColor(currentTemplateColor, getCurrentShapeBuilder().getShapeColor());
                                if (uiColor == Color.TRANSPARENT) {
                                    return;
                                } else {
                                    List<ColorItemPool.Point> points = currentShape.getShape().getColorItem().getData();
                                    for (int i = 0; i < points.size(); i++) {
                                        ColorItemPool.Point point = points.get(i);
                                        if (point.getX() == (w * times) && point.getY() == (h * times)) {
                                            Log.d(TAG, "return now");
                                            return;
                                        }
                                    }
//                                    currentShape.getShape().moveShape(x, y);
                                    currentShape.getShape().moveShape(xf, yf);
                                }
                            }
                            return;
                        }
//                    }
                    } catch (ArrayIndexOutOfBoundsException e) {
                        e.printStackTrace();
                    }
                }else {
                    return;
                }
            }
        }



//        if (templateBitmap != null) {
//            int searchCount = 1;
//            int indexW = 0;
//            int indexH = 0;
//
//            // 1、计算图片与模板比例
//            int length = widthNum * heightNum;
//            int pixelIndex = 0;
//            int h;
//            int w;
//            if (length >= templateColor.length) {
//                //放大：布局与模板比
//                h = (int) (yf / times);
//                w = (int) (xf / times);
//            } else {
//                //缩小：布局与模板比
//                h = (int) (yf * times);
//                w = (int) (xf * times);
//            }
//            while (searchCount < 4) {
//                h = h + indexH;
//                w = w + indexW;
//                pixelIndex = (h - 1) * templateBitmap.getWidth() + w;
//                if (pixelIndex < templateColor.length) {
//                    try {
//                        int currentTemplateColor = templateColor[pixelIndex];
//                        // 排除空白区域
//                        if (blankColor.contains(currentTemplateColor)) {
//                            Log.d(TAG, "fit blank pixel searchCount="+searchCount);
//
//                            if (length >= templateColor.length) {
//                                //放大：布局与模板比
////                                h = (int) (yf / times) + indexH;
////                                w = (int) (xf / times) + indexW;
//                                yf = (h - indexH)*times;
//                                xf = (w - indexW)*times;
//                            } else {
//                                //缩小：布局与模板比
////                                h = (int) (yf * times);
////                                w = (int) (xf * times);
//                                yf = (h - indexH) / times;
//                                xf = (w - indexW) / times;
//                            }
//                            if (searchCount == 1) {
//                                indexH = 1;
//                                indexW = 0;
//                            } else if (searchCount == 2) {
//                                indexH = 0;
//                                indexW = 1;
//                            } else if (searchCount == 3) {
//                                indexH = 1;
//                                indexW = 1;
//                            } else if (searchCount == 4) {
//                                indexH = -1;
//                                indexW = 0;
//                            } else if (searchCount == 5) {
//                                indexH = 0;
//                                indexW = -1;
//                            } else if (searchCount == 6) {
//                                indexH = -1;
//                                indexW = -1;
//                            }
//                            searchCount++;
//                            continue;
//                        } else {
//                            if (currentShape != null && currentShape.getShape() != null) {
//                                int uiColor = ISDColorUtil.getIsdUIColor(currentTemplateColor, getCurrentShapeBuilder().getShapeColor());
//                                if (uiColor == Color.TRANSPARENT) {
//                                    return;
//                                } else {
//                                    List<ColorItemPool.Point> points = currentShape.getShape().getColorItem().getData();
//                                    for (int i = 0; i < points.size(); i++) {
//                                        ColorItemPool.Point point = points.get(i);
//                                        if (point.getX() == (w * times) && point.getY() == (h * times)) {
//                                            Log.d(TAG, "return now");
//                                            return;
//                                        }
//                                    }
////                                    currentShape.getShape().moveShape(x, y);
//                                    currentShape.getShape().moveShape(xf, yf);
//                                }
//                            }
//                            return;
//                        }
////                    }
//                    } catch (ArrayIndexOutOfBoundsException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }
//        }
    }

    @Override
    @SuppressLint("VisibleForTests")
    public void createShape() {
        final AbstractShape shape;
        Paint paint = createPaint(isErasing);
        Template template = new Template(templateColor, blankColor, density, times, templateBitmap, isFront);
        if (isErasing) {
            shape = new PixelBrushShape(new ColorItemPool(widthNum + 1, heightNum + 1), radius, widthNum, heightNum, getCurrentShapeBuilder(), staticRect, staticColor, template);
            paint = createEraserPaint();
        } else if (currentShapeBuilder.getShapeType() == ShapeType.OVAL) {
            shape = new OvalShape();
        } else if (currentShapeBuilder.getShapeType() == ShapeType.RECTANGLE) {
            shape = new RectangleShape();
        } else if (currentShapeBuilder.getShapeType() == ShapeType.LINE) {
            shape = new LineShape();
        } else {
            shape = new PixelBrushShape(new ColorItemPool(widthNum + 1, heightNum + 1), radius, widthNum, heightNum, getCurrentShapeBuilder(), staticRect, staticColor, template);
        }
        currentShape = new ShapeAndPaint(shape, paint);
        drawShapes.push(currentShape);

        if (viewChangeListener != null) {
            viewChangeListener.onStartDrawing();
        }
    }

    @Override
    public Paint createPaint(boolean isErasing) {
        paint = new Paint();
        paint.setAntiAlias(true);
//        paint.setDither(true);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
//        paint.setStrokeJoin(Paint.Join.MITER);
        paint.setStrokeCap(Paint.Cap.BUTT);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

        // apply shape builder parameters
        if (isErasing) {
            paint.setStrokeWidth(currentShapeBuilder.getShapeSize());
            paint.setAlpha(currentShapeBuilder.getShapeOpacity());
        }
        paint.setColor(currentShapeBuilder.getShapeColor());

        return paint;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public void setTemplateColor(int[] templateColor) {
        this.templateColor = templateColor;
    }

    public void setBlankColor(List<Integer> blankColor) {
        this.blankColor = blankColor;
    }

    public void setCacheColorIndex(Map<Integer, Point> cacheColorIndex) {
        this.cacheColorIndex = cacheColorIndex;
    }

    public void setDensity(float density) {
        this.density = density;
    }

    public void setTemplateBitmap(Bitmap templateBitmap) {
        this.templateBitmap = templateBitmap;
    }

    public void setFront(boolean front) {
        isFront = front;
    }


    @Override
    public void endShape(float touchX, float touchY) {
        if (currentShape.getShape().hasBeenTapped()) {
            // just a tap, this is not a shape, so remove it
            drawShapes.remove(currentShape);
            //handleTap(touchX, touchY);
        }

        if (viewChangeListener != null) {
            viewChangeListener.onStopDrawing();
            viewChangeListener.onViewAdd(this);
        }
    }
    @Override
    public boolean redo() {
        if (!redoShapes.empty()) {
            drawShapes.push(redoShapes.pop());
            invalidate();
        }

        if (viewChangeListener != null) {
            viewChangeListener.onViewAdd(this);
        }
        return !redoShapes.empty();
    }
    @Override
    // region eraser
    public void brushEraser() {
        isEnabled = true;
        isErasing = true;
    }
    @Override
    public void setBrushEraserSize(float brushEraserSize) {
        mBrushEraserSize = brushEraserSize;
    }
    @Override
    public float getEraserSize() {
        return mBrushEraserSize;
    }
    // endregion
    @Override
    // region Setters/Getters
    public void setShapeBuilder(ShapeBuilder shapeBuilder) {
        currentShapeBuilder = shapeBuilder;
    }
    @Override
    public void enableDrawing(boolean brushDrawMode) {
        isEnabled = brushDrawMode;
        isErasing = !brushDrawMode;
        if (brushDrawMode) {
            setVisibility(View.VISIBLE);
        }
    }
    @Override
    public boolean isDrawingEnabled() {
        return isEnabled;
    }
    @Override
    @VisibleForTesting
    public ShapeAndPaint getCurrentShape() {
        return currentShape;
    }
    @Override
    @VisibleForTesting
    public ShapeBuilder getCurrentShapeBuilder() {
        return currentShapeBuilder;
    }

    @Override
    public void clearAll() {
        drawShapes.clear();
        redoShapes.clear();
        invalidate();
    }
    @Override
    public boolean undo() {
        if (!drawShapes.empty()) {
            redoShapes.push(drawShapes.pop());
            invalidate();
        }
        if (viewChangeListener != null) {
            viewChangeListener.onViewRemoved(this);
        }
        return !drawShapes.empty();
    }

}

